package com.huan.xunlei1;

import com.huan.Test3_TimeServer;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

public class Xunlei {
    private static Logger logger = Logger.getLogger(Test3_TimeServer.class.getName());

//    private static volatile long size = 0;//long型数据不能保证原子性
    private static long downloadedFileSize = 0;//使用重量级锁机制，性能降低
//    private static AtomicLong downloadedFileSize = new AtomicLong(0L);//使用原子类，保证线程安全

    public static void main(String[] args) throws IOException, InterruptedException {
        List<Thread> list = new ArrayList<>();
        long start = System.currentTimeMillis();
        //1.获取要下载的文件的大小
        String url = "http://www.hostbuf.com/downloads/finalshell_install.exe";
        long fileSize = getDownLoadFileSize(url);
        logger.info("待下载文件大小："+fileSize);

        //2.新文件保存的位置
        String fileName = getFileName(url);
        logger.info("待下载文件名："+fileName);
        String userHome = "e:";
        String path = userHome+ File.separator+fileName;//文件保存的位置
        logger.info("待下载文件保存位置："+path);

        //3.创建次新文件（空）
        //使用RandomAccessFile,创建一个空文件，占位
        RandomAccessFile randomAccessFile = new RandomAccessFile(path,"rw");
        randomAccessFile.setLength(fileSize);//设置文件大小
        randomAccessFile.close();
        logger.info("创建新文件成功");

        //4.获取本机的核数 -》 线程数
        int cpuNum = Runtime.getRuntime().availableProcessors();//获取CPU的核数
        logger.info("CPU的核数："+cpuNum);

        //5.计算每个线程要下载的字节数
        long blockSize = getSizePerThread(fileSize,cpuNum);
        logger.info("每个线程要下载的字节数："+blockSize);

        //6.循环创建线程，每个线程下载自己的部分
        //使用回调函数，获取每个线程下载的进度
        //要使用锁机制，保证线程安全，但是会导致性能降低
        for (int i = 0; i < cpuNum; i++) {
//            long startPos = i*blockSize;
//            long endPos = (i+1)*blockSize-1;
//            if(i==cpuNum-1){//最后一个线程
//                endPos = fileSize-1;
//            }
            DownloadedSizeNotify downloadSizeNotify = new DownloadedSizeNotify() {
                @Override
                public void notifySize(long size) {
                    //加锁方案
                    //用时 79 571
                    synchronized (Xunlei.class){
                        downloadedFileSize += size;
                        System.out.println("下载进度："+downloadedFileSize);
                    }
                    //使用原子类
                    //用时 80 022
//                    downloadedFileSize.addAndGet(Long.valueOf(size));
                }
            };
            //将回调函数传递给线程
            DownloadTask task = new DownloadTask(i,fileSize,cpuNum,blockSize,url,path,downloadSizeNotify);
            Thread thread = new Thread(task);
            thread.start();
            list.add(thread);
        }
        for (Thread thread : list)
            thread.join();
        System.out.println("下载完成");
        long end = System.currentTimeMillis();
        System.out.println(
                "下载耗时："+(end-start)
        );
    }

    /**
     * 获取每个线程要下载的字节数
     * @param fileSize
     * @param cpuNum
     * @return
     */
    private static long getSizePerThread(long fileSize, int cpuNum) {
        //每个线程下载的字节数
        //如果fileSize不能整除cpuNum，每个线程下载的字节数为fileSize/cpuNum+1，要多一个字节
        return fileSize%cpuNum==0?fileSize/cpuNum:fileSize/cpuNum+1;
    }

    private static long getDownLoadFileSize(String url) throws IOException {
        long fileSize = 0;
        URL url1 = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) url1.openConnection();

        //设置请求头，获取内容大小
        //仅接收请求头
        connection.setRequestMethod("HEAD");
        //设置超时连接
        connection.setConnectTimeout(3000);
        //连接
        connection.connect();
        //获取内容大小
        int contentLength = connection.getContentLength();
        logger.info("文件大小："+contentLength);
        return contentLength;
    }

    private static String getFileName(String url){
        Date date = new Date();
        DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String prefix = dateFormat.format(date);
        String suffix = url.substring(url.lastIndexOf("."));
        return prefix+suffix;
    }
}

interface DownloadedSizeNotify{
    public void notifySize(long size);
}
